/**
 * Copyright (c) 2012-2016, www.tinygroup.org (luo_guo@icloud.com).
 * <p>
 * Licensed under the GPL, Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.gnu.org/licenses/gpl.html
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.tinygroup.database.initdata.impl;

import org.tinygroup.commons.tools.CollectionUtil;
import org.tinygroup.database.ProcessorManager;
import org.tinygroup.database.config.initdata.InitData;
import org.tinygroup.database.config.initdata.InitDatas;
import org.tinygroup.database.config.initdata.Record;
import org.tinygroup.database.config.table.Table;
import org.tinygroup.database.initdata.InitDataProcessor;
import org.tinygroup.database.initdata.InitDataSqlProcessor;
import org.tinygroup.database.util.DataBaseUtil;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;
import org.tinygroup.metadata.util.MetadataUtil;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;


/**
 * @author chenjiao
 */
public class InitDataProcessorImpl implements InitDataProcessor {
    /**
     * Map<包名,Map<表名,InitData>>
     */
    private static Map<String, InitData> initDatasIdMap = new HashMap<String, InitData>();
    private static Map<String, InitData> initDatasNameMap = new HashMap<String, InitData>();
    private static InitDataProcessor initDataProcessor = new InitDataProcessorImpl();

    private static Logger logger = LoggerFactory
            .getLogger(InitDataProcessorImpl.class);
    private ProcessorManager processorManager;

    private Map<String,InitData> tableInitDataMap = new HashMap<String, InitData>();

    public static InitDataProcessor getInitDataProcessor() {
        return initDataProcessor;
    }

    public ProcessorManager getProcessorManager() {
        return processorManager;
    }

    public void setProcessorManager(ProcessorManager processorManager) {
        this.processorManager = processorManager;
    }

    public List<String> getInitSql(String packageName, String tableName,
                                   String language) {
        InitData tableInitData = getInitData(packageName, tableName); // 肯定不为空，为空的情况该函数会直接抛异常
        return getInitSql(tableInitData, language);
    }

    public List<String> getInitSql(String tableName, String language) {
        return getInitSql(null, tableName, language);
    }

    public List<String> getInitSqlByTableId(String tableId, String language) {
        InitData tableInitData = getInitDataByTableId(tableId);
        return getInitSql(tableInitData, language);
    }

    /**
     * 获取初始化sql(新增语句)
     * @param language
     * @return
     */
    public List<String> getInitSql(String language) throws SQLException {
        InitDataSqlProcessor sqlProcessor = (InitDataSqlProcessor) processorManager
                .getProcessor(language, "initData");
        List<String> list = new ArrayList<String>();
        //根据table的顺序遍历初始化数据
        List<Table> allOrderTables = DataBaseUtil.getTables(getClass().getClassLoader());
        for(Table table: allOrderTables){
            InitData tableInitData = initDatasIdMap.get(table.getId());
            if(tableInitData==null){
                continue;
            }
            list.addAll(sqlProcessor.getInitSql(tableInitData));
        }
        for(Table table:allOrderTables){
            list.addAll(sqlProcessor.getPostInitSql(table));
        }
        return list;
    }



    public List<String> getInitSql(InitData tableInitData, String language) {
        List<String> sqls = new ArrayList<String>();
        InitDataSqlProcessor sqlProcessor = (InitDataSqlProcessor) processorManager
                .getProcessor(language, "initData");
        sqls.addAll(sqlProcessor.getInitSql(tableInitData));
        return sqls;
    }

    public void addInitDatas(InitDatas initDatas) {
        logger.logMessage(LogLevel.DEBUG, "开始添加表格初始数据");
        if (initDatas == null || initDatas.getInitDataList() == null) {
            logger.logMessage(LogLevel.DEBUG, "传入的初始数据为空，数据添加结束。");
            return;
        }
        for (InitData initData : initDatas.getInitDataList()) {
            addInitData(initData);
        }
        logger.logMessage(LogLevel.DEBUG, "表格初始数据添加完毕");
    }

    public void removeInitDatas(InitDatas initDatas) {
        logger.logMessage(LogLevel.DEBUG, "开始添加表格初始数据");
        if (initDatas == null || initDatas.getInitDataList() == null) {
            logger.logMessage(LogLevel.DEBUG, "传入的初始数据为空，数据添加结束。");
            return;
        }
        for (InitData initData : initDatas.getInitDataList()) {
            removeInitData(initData);
        }
        logger.logMessage(LogLevel.DEBUG, "表格初始数据添加完毕");
    }

    /**
     * 添加初始化sql
     *
     * @param initData
     */
    private void removeInitData(InitData initData) {
        String tableId = MetadataUtil.passNull(initData.getTableId());
        String tableName = DataBaseUtil.getTableById(tableId, this.getClass().getClassLoader()).getName();
        logger.logMessage(LogLevel.DEBUG, "开始移除表格[表名:{0},表ID:{1}]的初始化数据", tableName, tableId);

        if (!CollectionUtil.isEmpty(initDatasIdMap)) {
            InitData tableInitData = initDatasIdMap.get(tableName);
            if (tableInitData != null) {
                tableInitData.getRecordList().removeAll(initData.getRecordList());
            }
        }
        initDatasIdMap.remove(tableId);
        logger.logMessage(LogLevel.DEBUG, "移除表格[表名:{0},表ID:{1}]的初始化数据完毕", tableName, tableId);
    }

    /**
     * 添加初始化sql
     *
     * @param initData
     */
    private void addInitData(InitData initData) {
        String tableId = MetadataUtil.passNull(initData.getTableId());
        Table table = DataBaseUtil.getTableById(tableId, this.getClass().getClassLoader());
        if (table == null) {
            throw new RuntimeException("表格" + initData.getTableId() + "不存在");
        }
        String tableName = table.getName();
        logger.logMessage(LogLevel.DEBUG, "开始为表格[表名:{0},表ID:{1}]添加初始化数据", tableName, tableId);
        if (!initDatasIdMap.containsKey(tableId)) {
            InitData tableInitData = new InitData();
            tableInitData.setTableId(tableId);
            tableInitData.setId(initData.getId());
            tableInitData.setRecordList(new ArrayList<Record>());
            initDatasIdMap.put(tableId, tableInitData);
            initDatasNameMap.put(tableName,tableInitData);
        }
        InitData tableInitData = initDatasIdMap.get(tableId);
        tableInitData.getRecordList().addAll(initData.getRecordList());


        logger.logMessage(LogLevel.DEBUG, "表格[表名:{0},表ID:{1}]添加初始化数据完毕", tableName, tableId);
    }

    public InitData getInitDataByTableId(String tableId) {
        return initDatasIdMap.get(tableId);
    }

    public InitData getInitData(String tableName) {
        return getInitData("", tableName);
    }

    public InitData getInitData(String packageName, String tableName) {
        String realTableName = MetadataUtil.passNull(tableName);
        logger.logMessage(LogLevel.DEBUG, "获取表格[表名:{0}]初始化数据", realTableName);
        if (initDatasNameMap.containsKey(tableName)) {
            logger.logMessage(LogLevel.DEBUG, "成功获取表格[表名:{0}]初始化数据", realTableName);
            return initDatasNameMap.get(realTableName);
        }
        logger.logMessage(LogLevel.DEBUG, "[包:{0}]下未找到[表名:{0}]的初始化数据", realTableName);
        if (initDatasNameMap.containsKey(realTableName)) {
            InitData initData = initDatasNameMap.get(realTableName);
            logger.logMessage(LogLevel.DEBUG,
                    "成功获取表格[包:{0},表名:{1}]初始化数据", realTableName);
            return initData;
        }
        throw new RuntimeException(String.format("获取表格[表名:%s]初始化数据失败", realTableName));

    }

    public List<String> getDeInitSql(String packageName, String tableName,
                                     String language) {
        InitData tableInitData = getInitData(packageName, tableName); // 肯定不为空，为空的情况该函数会直接抛异常
        return getDeInitSql(tableInitData, language);

    }

    public List<String> getDeInitSql(String tableName, String language) {
        return getDeInitSql(null, tableName, language);
    }

    /**
     * 初始化数据删除语句
     * @param language
     * @return
     */
    public List<String> getDeInitSql(String language) {

        InitDataSqlProcessor sqlProcessor = (InitDataSqlProcessor) processorManager
                .getProcessor(language, "initData");
        List<String> list = new ArrayList<String>();

        List<Table> allOrderTables = DataBaseUtil.getTables(getClass().getClassLoader());
        //没有表格信息直接返回空
        if(allOrderTables.size()==0){
            return list;
        }
/*        for(Table table:allOrderTables){
            list.addAll(sqlProcessor.getPreInitSql(table));
        }*/

        //根据table倒序遍历初始化数据
        for(int i=allOrderTables.size()-1;i>=0;i--){
            Table table = allOrderTables.get(i);
            InitData tableInitData = initDatasIdMap.get(table.getId());
            if(tableInitData==null){
                continue;
            }
            list.addAll(sqlProcessor.getDeInitSql(tableInitData));
        }
        return list;
    }

    private List<String> getDeInitSql(InitData tableInitData, String language) {
        List<String> sqls = new ArrayList<String>();
        InitDataSqlProcessor sqlProcessor = (InitDataSqlProcessor) processorManager
                .getProcessor(language, "initData");
        sqls.addAll(sqlProcessor.getDeInitSql(tableInitData));
        return sqls;
    }

    public List<String> getDeInitSqlByTableId(String tableId, String language) {
        InitData data = getInitDataByTableId(tableId);
        return getDeInitSql(data, language);
    }

    public List<InitData> getInitDatas() {
        List<InitData> initDatas = new ArrayList<InitData>();
        initDatas.addAll(initDatasIdMap.values());
        return initDatas;
    }


}
